﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Willow.Extensions;
using Willow.IRepository;

namespace Willow.EFRepository
{
    public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        protected readonly DbContext Context;

        public BaseRepository(DbContext context)
        {
            Context = context;
        }

        public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> expression)
        {
            return All().FirstOrDefault(expression);
        }

        /// <summary>
        /// 获取所有对象
        /// </summary>
        /// <returns></returns>
        public IQueryable<TEntity> All()
        {
            return Context.Set<TEntity>().AsQueryable();
        }

        /// <summary>
        /// 根据条件获取对象集合
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        public virtual IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> predicate)
        {
            return Context.Set<TEntity>().Where<TEntity>(predicate).AsQueryable<TEntity>();
        }

        /// <summary>
        /// 根据条件获取对象集合
        /// </summary>
        /// <param name="filter">The filter.</param>
        /// <param name="total">The total.</param>
        /// <param name="index">The index.</param>
        /// <param name="size">The size.</param>
        /// <returns></returns>
        public virtual IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> filter, out int total, int index = 0,
                                               int size = 50)
        {
            return this.Filter(filter, out total, index, size, null);
        }

        /// <summary>
        /// 根据条件获取对象集合
        /// </summary>
        /// <param name="filter">The filter.</param>
        /// <param name="total">The total.</param>
        /// <param name="index">The index.</param>
        /// <param name="size">The size.</param>
        /// <param name="sortConditions">The sort conditions.</param>
        /// <returns></returns>
        public IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> filter, out int total, int index = 1, int size = 50, params PropertySortCondition[] sortConditions)
        {
            //拼接过滤条件
            var resetSet = filter != null
                                ? Context.Set<TEntity>().Where<TEntity>(filter).AsQueryable()
                                : Context.Set<TEntity>().AsQueryable();

            if (sortConditions != null && sortConditions.Length > 0)
            {
                //拼接排序条件
                IOrderedQueryable<TEntity> orderSource = null;
                for (int i = 0; i < sortConditions.Length; i++)
                {
                    var sortCondition = sortConditions[i];
                    orderSource = i == 0
                        ? resetSet.OrderBy(sortCondition.PropertyName, sortCondition.ListSortDirection)
                        : orderSource.ThenBy(sortCondition.PropertyName, sortCondition.ListSortDirection);
                }
                resetSet = orderSource;
            }
            else
            {
                //默认按主键排序
                resetSet = resetSet.OrderBy(FindPrimaryKeyProperty().Name);
            }


            total = resetSet.Count();

            var skipCount = (index - 1) * size;
            //拼接分页条件
            resetSet = skipCount == 0 ? resetSet.Take(size) : resetSet.Skip(skipCount).Take(size);
            return resetSet.AsQueryable();
        }

        /// <summary>
        /// 创建对象
        /// </summary>
        /// <param name="TObject">The t object.</param>
        public virtual void Create(TEntity TObject)
        {
            Context.Set<TEntity>().Add(TObject);
        }

        /// <summary>
        /// 删除对象
        /// </summary>
        /// <param name="TObject">The t object.</param>
        public virtual void Delete(TEntity TObject)
        {
            Context.Set<TEntity>().Remove(TObject);
        }

        /// <summary>
        /// 修改对象
        /// </summary>
        /// <param name="TObject">The t object.</param>
        public virtual void Update(TEntity TObject)
        {
            try
            {
                var entry = Context.Entry(TObject);
                Context.Set<TEntity>().Attach(TObject);
                entry.State = EntityState.Modified;
            }
            catch (OptimisticConcurrencyException ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 删除对象
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        public virtual int Delete(Expression<Func<TEntity, bool>> predicate)
        {
            var objects = Filter(predicate);
            foreach (var obj in objects)
                Context.Set<TEntity>().Remove(obj);
            return Context.SaveChanges();
        }

        /// <summary>
        /// 根据条件判断对象是否已存在
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        public bool Contains(Expression<Func<TEntity, bool>> predicate)
        {
            return Context.Set<TEntity>().Any(predicate);
        }

        /// <summary>
        /// 根据主键获取对象
        /// </summary>
        /// <param name="keys">The keys.</param>
        /// <returns></returns>
        public virtual TEntity Find(params object[] keys)
        {
            return Context.Set<TEntity>().Find(keys);
        }

        /// <summary>
        /// 保存对象(创建或修改)
        /// </summary>
        /// <param name="TObject">The t object.</param>
        public void Save(TEntity TObject)
        {
            if (IsTransient(TObject))
            {
                InitPrimaryKey(TObject);
                Create(TObject);
            }
            else
            {
                Update(TObject);
            }
        }

        /// <summary>
        /// 判断对象是否是临时的
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentNullException">entity</exception>
        private bool IsTransient(TEntity entity)
        {
            if (entity == null) throw new ArgumentNullException("entity");

            var propertyInfo = FindPrimaryKeyProperty();
            var propertyType = propertyInfo.PropertyType;
            //获取默认值
            var transientValue = propertyType.IsValueType ?
                Activator.CreateInstance(propertyType) : null;
            //判断主键是否有值
            return Equals(propertyInfo.GetValue(entity, null), transientValue);
        }

        /// <summary>
        /// 获取对象主键属性
        /// </summary>
        /// <returns></returns>
        private PropertyInfo FindPrimaryKeyProperty()
        {
            //查找主键
            var objectContext = ((IObjectContextAdapter)Context).ObjectContext;
            var objectSet = objectContext.CreateObjectSet<TEntity>();
            var elementType = objectSet.EntitySet.ElementType;
            var pk = elementType.KeyMembers.First();
            //获取主键属性
            var propertyInfo = typeof(TEntity).GetProperty(pk.Name);
            return propertyInfo;
        }

        /// <summary>
        /// 初始化主键
        /// </summary>
        /// <param name="entity">The entity.</param>
        private void InitPrimaryKey(TEntity entity)
        {
            var primary = this.FindPrimaryKeyProperty();
            primary.SetValue(entity, Guid.NewGuid());
        }

    }
}
